﻿using System.Reflection;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using JSon = System.Text.Json.JsonSerializer;

namespace Lib.Serialization;
/// <summary>
/// 序列化与反序列化
/// </summary>
public static partial class JavaScript {
    /// <summary>
    /// 静态构造函数
    /// </summary>
    static JavaScript() {
        Initialize(JavaScriptOptions = new ());
    }
    /// <summary>
    /// 序列化选项
    /// </summary>
    static JsonSerializerOptions JavaScriptOptions { get; set; }
    /// <summary>
    /// 
    /// </summary>
    private static void Initialize(JsonSerializerOptions Opts) {
        //
        Opts ??= new ();
        //
        Opts.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
        // 不使用驼峰样式
        Opts.PropertyNamingPolicy = null;
        // 忽略循环引用
        Opts.ReferenceHandler = ReferenceHandler.IgnoreCycles;
        // 处理注释
        Opts.ReadCommentHandling = JsonCommentHandling.Skip;
        // 是否忽略大小写
        Opts.PropertyNameCaseInsensitive = true;
        // 是否缩进
        Opts.WriteIndented = true;
        // 处理 DateTime 类型
        Opts.Converters.Add(new DateTimeConverter());
        // 处理 DateTime? 类型
        Opts.Converters.Add(new DateTimeNullableConverter());
        // 处理 Type 类型
        Opts.Converters.Add(new TypeConverter());
        // 处理 Assembly 类型序列化
        Opts.Converters.Add(new AssemblyConverter());
        // 处理 Enum 类型序列化
        Opts.Converters.Add(new JsonStringEnumConverter());
    }


    #region "对象序列化"

    /// <summary>
    /// 序列化，支持中文与日期格式化
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="Object">要序列化对象</param>
    /// <param name="Properties">
    /// <para>1.自定义输出名称："UID,Name:FullName,Password,Sex,Age"，格式如：字段原名:别名;</para>
    /// <para>2.要忽略的字段列："@,Password,Age"</para>
    /// </param>
    /// <param name="Action"></param>
    /// <returns></returns>
    public static string Serialize<T>(this T Object, string Properties = null, Action<JsonSerializerOptions> Action = null) {
        if (Object == null) {
            return null;
        }
        try {
            JsonSerializerOptions Opts = null;
            Action?.Invoke(Opts = new());
            return JSon.Serialize(Object, Initialize(typeof(T), Properties, Opts));
        } catch {
            return default;
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    private static JsonSerializerOptions Initialize(Type Type, string Properties = null, JsonSerializerOptions Opts = null) {
        if (!string.IsNullOrEmpty(Properties)) {
            if (Type.IsArray) {
                Type = Type.GetElementType();
            } else {
                if (Type.Name == "List`1" || Type.Name == "IList`1") {
                    Type = Type.GetGenericArguments()[0];
                }
            }
            Opts ??= new();
            var Ignore = false;
            if (Properties.StartsWith('@')) {
                Ignore = true;
            }
            var Propertys = Properties.Split(',').ToList();
            if (Ignore) {
                Propertys.RemoveAt(0);
            }
            var Columns = new Dictionary<string, string>();
            foreach (var Column in Propertys) {
                if (Column.Contains(':')) {
                    var Keys = Column.Split(":"); Columns.Add(Keys[0], Keys[1]); 
                } else {
                    Columns.Add(Column, null);
                }
            }
            if (Type.IsClass && Type != typeof(string)) {
                Opts.TypeInfoResolver = new DefaultJsonTypeInfoResolver {
                    Modifiers = { (JSonType)=> {
                        if (JSonType.Type.IsAssignableTo(Type)) {
                            foreach(var Rs in JSonType.Properties) {
                                Rs.ShouldSerialize = (e, value) => {
                                    if (Ignore) {
                                      return !Columns.ContainsKey(Rs.Name);
                                    }
                                    return Columns.ContainsKey(Rs.Name);
                                };
                            }
                        }

                    }}
                };
            }
        }
        if (Opts != null) {
            Initialize(Opts);
        } else {
            Opts = JavaScriptOptions;
        }
        return Opts;
    }

    #endregion

    #region "对象反序列化"

    /// <summary>
    /// 反序列化
    /// </summary>
    public static T Deserialize<T>(string Value) {
        if (string.IsNullOrEmpty(Value)) {
            return default;
        }
        try {
            return JSon.Deserialize<T>(Value);
        } catch {
            return default;
        }

    }
    /// <summary>
    /// 反序列化
    /// </summary>
    public static object Deserialize(string Value, Type Type) {
        if (string.IsNullOrEmpty(Value)) {
            return default;
        }
        try {
            return JSon.Deserialize(Value, Type);
        } catch {
            return default;
        }
    }

    #endregion

    #region "JSon 字符串压缩与格式化"

    /// <summary>
    /// JSon字符串压缩
    /// </summary>
    public static string Compress(string Value) {
        var sb = new StringBuilder();
        using (StringReader Reader = new (Value)) {
            int Char = -1, Last = -1;
            var IsContinue = false;
            while ((Char = Reader.Read()) > -1) {
                if ((char)Last != '\\' && (char)Char == '\"') {
                    if (!IsContinue) {
                        IsContinue = true;
                    } else {
                        IsContinue = false;
                    }
                }
                if (!char.IsWhiteSpace((char)Char) || IsContinue) {
                    sb.Append((char)Char);
                }
                Last = Char;
            }
        }
        return sb.ToString();
    }
    /// <summary>
    /// JSon 字符串格式化（美化）
    /// </summary>
    public static string Format(string Value) {
        var Space = "  ";
        var Index = 0;
        var Quote = false;
        var sb = new StringBuilder();
        for (var i = 0; i < Value.Length; i++) {
            var Char = Value[i];
            switch (Char) {
                case '{':
                case '[':
                    sb.Append(Char);
                    if (!Quote) {
                        sb.AppendLine(); Enumerable.Range(0, ++Index).ForEach(item => sb.Append(Space));
                    }
                    break;
                case '}':
                case ']':
                    if (!Quote) {
                        sb.AppendLine(); Enumerable.Range(0, --Index).ForEach(item => sb.Append(Space));
                    }
                    sb.Append(Char);
                    break;
                case '"':
                    sb.Append(Char);
                    var EScape = false;
                    var Number = i;
                    while (Number > 0 && Value[--Number] == '\\') {
                        EScape = !EScape;
                    }
                    if (!EScape) {
                        Quote = !Quote;
                    }
                    break;
                case ',':
                    sb.Append(Char);
                    if (!Quote) {
                        sb.AppendLine(); Enumerable.Range(0, Index).ForEach(item => sb.Append(Space));
                    }
                    break;
                case ':':
                    sb.Append(Char);
                    if (!Quote) {
                        sb.Append(' ');
                    }
                    break;
                default:
                    sb.Append(Char);
                    break;
            }
        }
        return sb.ToString();
    }
    /// <summary>
    /// 
    /// </summary>
    internal static void ForEach<T>(this IEnumerable<T> e, Action<T> Action) {
        foreach (var i in e) {
            Action(i);
        }
    }

    #endregion

}



